﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Data;
using Microsoft.Bing.Service;

namespace Microsoft.Bing.Application
{
    /// <summary>
    /// Implements a stock <see cref="System.Windows.Controls.UserControl"/> to demonstrate and serve as working implementation for the
    /// Microsoft Bing Search API and the Microsoft Silverlight SDK for Bing
    /// </summary>
    /// <example>
    /// <para>
    /// The Microsoft Bing SDK is very easy to use in terms of Xaml implementation and binding to the Search data itself. The <see cref="Microsoft.Bing.SearchEnvironment"/> singleton is where
    /// the bulk of the logic and data operations take place. This object is also a <see cref="System.Windows.DependencyObject"/> and implements
    /// <see cref="System.ComponentModel.INotifyPropertyChanged"/>, which provides support for data binding. 
    /// </para>
    /// <para>
    /// To execute a search, you only need to create a <see cref="Microsoft.Bing.Service.SearchRequest"/>, populate its Query property, and
    /// then insure that you have set the SearchMedia property on the <see cref="Microsoft.Bing.SearchEnvironment"/>. Then, call the method
    /// <see cref="Microsoft.Bing.SearchEnvironment.BeginSearch"/>, insuring that you have provided a handler for <see cref="Microsoft.Bing.SearchEnvironment.SearchCompleted"/>.
    /// </para>
    /// <para>
    /// You should also bind any display controls - typically <see cref="System.Windows.Controls.ItemsControl"/> instances, such as ListBox etc - 
    /// to the <see cref="SearchEnvironment.Results"/> property. This property is of type <see cref="Microsoft.Bing.SearchResults"/> and is an observable collection.
    /// This means that you can data bind your <see cref="System.Windows.Controls.ItemsControl"/> instances in advance, like this:
    /// </para>
    /// <code>
    /// //Assumes a ItemsControl named "MyList" exists...
    /// MyList.ItemsSource = SearchEnvironment.Default.Results;
    /// 
    /// //Consume SearchEnvironment Events
    /// SearchEnvironment.Default.SearchStarted += new EventHandler(OnSearchStarted);
    /// SearchEnvironment.Default.SearchCompleted += new EventHandler&lt;SearchResponseEventArgs&gt;(OnSearchCompleted);
    /// SearchEnvironment.Default.Error += new EventHandler&lt;ErrorEventArgs&gt;(OnSearchError);
    /// 
    /// //Begin the Search (Assume a TextBox named "QueryText" exists)
    /// SearchEnvironment.Default.PageSize = 20;
    /// SearchEnvironment.Default.MediaType = SearchMedia.Web;
    /// SearchEnvironment.Default.BeginSearch(QueryText.Text);
    /// </code>
    /// <para>
    /// This should be done prior to executing any searches. Then when a search completes, the <see cref="SearchEnvironment.Results"/> collection will
    /// become populated, if there are avialable results, and your <see cref="System.Windows.Controls.ItemsControl"/> control will automatically data bind
    /// and use the asynchronous layout framework in the Silverlight runtime.
    /// </para>
    /// </example>
    /// <seealso cref="Microsoft.Bing.SearchEnvironment"/>
    /// <seealso cref="System.Windows.Controls.ItemsControl"/>
    public partial class Page : System.Windows.Controls.UserControl
    {
        /// <summary>
        /// Instantiates a new <see cref="Page"/> instance.
        /// </summary>
        public Page()
        {
            //Theme.Current.LoadTheme(new Uri("System.Windows.Controls.Views.Toolkit;component/Themes/generic.xaml", UriKind.Relative));
            InitializeComponent();

            //Search Type Button Content
            SearchWeb.LoadResource(typeof(ResourceControl), "System.Windows.Resources.SearchWeb24.xaml");
            SearchImages.LoadResource(typeof(ResourceControl), "System.Windows.Resources.SearchImages24.xaml");
            SearchVideo.LoadResource(typeof(ResourceControl), "System.Windows.Resources.SearchVideo24.xaml");
            SearchNews.LoadResource(typeof(ResourceControl), "System.Windows.Resources.SearchNews24.xaml");

            //View As Button Content
            ViewStack.LoadResource(typeof(ResourceControl), "System.Windows.Resources.ViewList.xaml");
            ViewTile.LoadResource(typeof(ResourceControl), "System.Windows.Resources.ViewTile.xaml");
            ViewCloud.LoadResource(typeof(ResourceControl), "System.Windows.Resources.ViewCloud.xaml");
            ViewBand.LoadResource(typeof(ResourceControl), "System.Windows.Resources.ViewBand.xaml");

            //Static Pager Buttons
            PageFirst.LoadResource(typeof(ResourceControl), "System.Windows.Resources.PageFirst.xaml");
            PageBack.LoadResource(typeof(ResourceControl), "System.Windows.Resources.PageBack.xaml");
            PageNext.LoadResource(typeof(ResourceControl), "System.Windows.Resources.PageNext.xaml");

            //ViewBar Events
            ViewToolbar.ButtonClicked += new EventHandler<ToolbarButtonEventArgs>(OnViewButtonClicked);
            ViewToolbar.SetButtonState(SearchWeb, true);
            ViewToolbar.SetButtonState(ViewStack, true);

            //Consume SearchBox Events
            QueryText.KeyDown += new KeyEventHandler(OnQueryKeyDown);
            SearchButton.SetValue(ToolTipService.ToolTipProperty, DataResources.SEARCH);

            //Consume SearchEnvironment Events
            SearchEnvironment.Default.SearchStarted += new EventHandler(OnSearchStarted);
            SearchEnvironment.Default.SearchCompleted += new EventHandler<SearchResponseEventArgs>(OnSearchCompleted);
            SearchEnvironment.Default.Error += new EventHandler<ErrorEventArgs>(OnSearchError);

            //Page Size Bindings
            Binding binding = new Binding("PageSize");
            binding.Mode = BindingMode.TwoWay;
            binding.Source = SearchEnvironment.Default;
            PageSize.SetBinding(Slider.ValueProperty, binding);
            PageSize.Value = 20;

            binding = new Binding("PageSize");
            binding.Mode = BindingMode.OneWay;
            binding.Source = SearchEnvironment.Default;
            PageSizeValueLabel.SetBinding(TextBlock.TextProperty, binding);
            
            //History
            HistoryList.ItemsSource = SearchEnvironment.Default.History;
            ClearHistoryButton.Click += new RoutedEventHandler(OnClearHistory);

            //Initial View
            ViewHost.SelectedIndex = 0;

            //Set Resource Strings
            ViewCaption.Text = SystemResources.SEARCH_RESULTS;
            NoResultsLabel.Text = SystemResources.NO_RESULTS;
            SearchWeb.SetValue(ToolTipService.ToolTipProperty, SystemResources.SEARCH_WEB);
            SearchImages.SetValue(ToolTipService.ToolTipProperty, SystemResources.SEARCH_IMAGES);
            SearchVideo.SetValue(ToolTipService.ToolTipProperty, SystemResources.SEARCH_VIDEO);
            SearchNews.SetValue(ToolTipService.ToolTipProperty, SystemResources.SEARCH_NEWS);
            ViewStack.SetValue(ToolTipService.ToolTipProperty, SystemResources.VIEW_STACK);
            ViewTile.SetValue(ToolTipService.ToolTipProperty, SystemResources.VIEW_TILE);
            ViewCloud.SetValue(ToolTipService.ToolTipProperty, SystemResources.VIEW_CLOUD);
            ViewBand.SetValue(ToolTipService.ToolTipProperty, SystemResources.VIEW_BAND);

            //Gather Pager Buttons
            pageButtons.Add(Page1);
            pageButtons.Add(Page2);
            pageButtons.Add(Page3);
            pageButtons.Add(Page4);
            pageButtons.Add(Page5);

            //Paging Events
            Pager.ButtonClicked += new EventHandler<ToolbarButtonEventArgs>(OnPagerButtonClicked);
            
            //Selection Consumption
            StackView.ItemSelected += new EventHandler<ItemControlEventArgs>(OnSearchItemSelected);
            TileView.ItemSelected += new EventHandler<ItemControlEventArgs>(OnSearchItemSelected);
            CloudView.ItemSelected += new EventHandler<ItemControlEventArgs>(OnSearchItemSelected);
            BandView.ItemSelected += new EventHandler<ItemControlEventArgs>(OnSearchItemSelected);

            //Observable Bindings
            StackView.ItemsSource = SearchEnvironment.Default.Results;
            TileView.ItemsSource = SearchEnvironment.Default.Results;
            CloudView.ItemsSource = SearchEnvironment.Default.Results;
            BandView.ItemsSource = SearchEnvironment.Default.Results;

            LiveLogo.MouseLeftButtonDown += new MouseButtonEventHandler(OnClickLogo);
        }

        void OnClearHistory(object sender, RoutedEventArgs e)
        {
            if (SearchEnvironment.Default.History != null) SearchEnvironment.Default.History.Clear();
        }

        void OnClickLogo(object sender, MouseButtonEventArgs e)
        {
            LinkLabel.OpenUrl(new Uri(DataResources.SEARCH_ROOT_URL, UriKind.Absolute), String.Empty);
        }

        /// <summary>
        /// Implements a virtual method which will be called when a search item is selected.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void OnSearchItemSelected(object sender, ItemControlEventArgs e)
        {
        }

        void OnPagerButtonClicked(object sender, ToolbarButtonEventArgs e)
        {
            if (e.Button != null)
            {
                if (e.Button.Value != null && String.Compare(e.Button.Value.ToString(), "First") == 0)
                {
                    SearchEnvironment.Default.PageFirst();
                }
                else if (e.Button.Value != null && String.Compare(e.Button.Value.ToString(), "Back") == 0)
                {
                    SearchEnvironment.Default.PageBack();
                }
                else if (e.Button.Value != null && String.Compare(e.Button.Value.ToString(), "Next") == 0)
                {
                    SearchEnvironment.Default.PageNext();
                }
                else if (e.Button.Value is long)
                {
                    long pageIndex = (long)e.Button.Value;
                    SearchEnvironment.Default.PageTo((uint)pageIndex);
                }
            }
        }

        private List<ToolbarButton>     pageButtons = new List<ToolbarButton>();

        void OnSearchStarted(object sender, EventArgs e)
        {
            ViewHost.ShowProgress(true);
        }

        void OnQueryKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter && !String.IsNullOrEmpty(QueryText.Text))
            {
                SearchEnvironment.Default.BeginSearch(QueryText.Text);
            }
        }

        /// <summary>
        /// Implements a virtual handler which is called when a search is to be executed
        /// </summary>
        /// <param name="sender">The sender</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> event arguments</param>
        protected virtual void OnExecuteSearch(object sender, RoutedEventArgs e)
        {
            if (!String.IsNullOrEmpty(QueryText.Text))
            {
                SearchEnvironment.Default.BeginSearch(QueryText.Text);
            }
        }

        /// <summary>
        /// Implements a virtual handler which is called when a search error occurs
        /// </summary>
        /// <param name="sender">The sender</param>
        /// <param name="e">The <see cref="Microsoft.Bing.ErrorEventArgs"/> event arguments</param>
        protected virtual void OnSearchError(object sender, ErrorEventArgs e)
        {
            SetPagingState();
            ViewHost.ShowProgress(false);
            MessageBox.Show(e.Error.Message, "Search Error", MessageBoxButton.OK);
        }

        /// <summary>
        /// Implements a virtual handler which is called when a search is completed
        /// </summary>
        /// <param name="sender">The sender</param>
        /// <param name="e">The <see cref="Microsoft.Bing.SearchResponseEventArgs"/> event arguments</param>
        protected virtual void OnSearchCompleted(object sender, SearchResponseEventArgs e)
        {
            SetPagingState();
            ViewHost.ShowProgress(false);
        }

        /// <summary>
        /// Implements a virtual handler which sets the current paging state based on the state of the <see cref="Microsoft.Bing.SearchEnvironment"/>.
        /// </summary>
        protected virtual void SetPagingState()
        {
            var resultCount = Math.Min(1000, SearchEnvironment.Default.ResultCount);

            var pageCount = resultCount / SearchEnvironment.Default.PageSize;

            ResultCountLabel.Text = String.Format(SystemResources.FMT_SEARCH_RESULT_COUNT, SearchEnvironment.Default.ResultCount, SearchEnvironment.Default.QueryTimeElapsed.Seconds, SearchEnvironment.Default.QueryTimeElapsed.Milliseconds);
            PageCountLabel.Text = String.Format(SystemResources.FMT_SEARCH_PAGE_COUNT, SearchEnvironment.Default.CurrentPage, pageCount);
            PageCountLabel.Visibility = ResultCountLabel.Visibility = Pager.Visibility = (SearchEnvironment.Default.ResultCount > 0 ? Visibility.Visible : Visibility.Collapsed);
            NoResultsLabel.Visibility = (SearchEnvironment.Default.ResultCount == 0 ? Visibility.Visible : Visibility.Collapsed);

            for (int index = 0; index < pageButtons.Count; index++)
            {
                long pageIndex = index + SearchEnvironment.Default.CurrentPage;
                pageButtons[index].Visibility = (pageIndex <= pageCount ? Visibility.Visible : Visibility.Collapsed);
                pageButtons[index].Content = pageIndex.ToString();
                pageButtons[index].Value = pageIndex;
                pageButtons[index].IsPressed = (SearchEnvironment.Default.CurrentPage == pageIndex);
            }
        }

        void OnHistoryLinkClick(object sender, RoutedEventArgs e)
        {
            if (sender is HyperlinkButton)
            {
                HyperlinkButton link = (HyperlinkButton)sender;
                if (link.DataContext is SearchHistoryItem)
                {
                    QueryText.Text = ((SearchHistoryItem)link.DataContext).Query;
                    SearchEnvironment.Default.BeginSearch(QueryText.Text);
                }
            }
        }

        void OnViewButtonClicked(object sender, ToolbarButtonEventArgs e)
        {
            if (e.Button.GroupName == SearchWeb.GroupName)
            {
                if (SearchWeb.IsPressed)
                {
                    SearchEnvironment.Default.MediaType = SearchMedia.Web;
                    VerifySearch();
                }
                else if (SearchImages.IsPressed)
                {
                    SearchEnvironment.Default.MediaType = SearchMedia.Images;
                    VerifySearch();
                }
                else if (SearchVideo.IsPressed)
                {
                    SearchEnvironment.Default.MediaType = SearchMedia.Video;
                    VerifySearch();
                }
                else if (SearchNews.IsPressed)
                {
                    SearchEnvironment.Default.MediaType = SearchMedia.News;
                    VerifySearch();
                }
            }
            else if (e.Button.GroupName == ViewStack.GroupName)
            {
                if (ViewStack.IsPressed)
                {
                    ViewHost.SelectedIndex = 0;
                }
                else if (ViewTile.IsPressed)
                {
                    ViewHost.SelectedIndex = 1;
                }
                else if (ViewCloud.IsPressed)
                {
                    ViewHost.SelectedIndex = 2;
                }
                else if (ViewBand.IsPressed)
                {
                    ViewHost.SelectedIndex = 3;
                }
            }
        }

        private void VerifySearch()
        {
            if (SearchEnvironment.Default.HasActiveSearch)
            {
                SearchEnvironment.Default.PageTo(SearchEnvironment.Default.CurrentPage);
            }
            else if (!String.IsNullOrEmpty(QueryText.Text))
            {
                SearchEnvironment.Default.BeginSearch(QueryText.Text);
            }
        }
    }
}
